﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using CSJ2K.Util.IO;

namespace CSJ2K.Jpc.Segments
{
    /// <summary>Image and Tile Size (SIZ)</summary>
    /// <comment>
    /// Function: Provides information about the uncompressed image such as the width and 
    ///      height of the reference grid, the width and height of the tiles, the number of 
    ///      components, component bit depth, and the separation of component samples with 
    ///      respect to the reference grid. 
    /// Usage: Main header. There shall be one and only one in the main header immediately 
    ///      after the SOC marker segment. There shall be only one SIZ per codestream. 
    /// Length: Variable depending on the number of components. 
    /// </comment>
    public class SIZ : Segment 
    {
        public enum RValues : ushort
        {
            Standard = 0x0000,
            Restricted0 = 0x0001,
            Restricted1 = 0x0002
        }
        /// <summary>Denotes capabilities that a decoder needs to properly decode the codestream.</summary>
        public RValues R;

        /// <summary>Width of the reference grid.</summary>
        public uint X;	   

        /// <summary>Height of the reference grid.</summary>
        public uint Y;

        /// <summary>Horizontal offset from the origin of the reference grid to the left side of the image area.</summary>
        public uint XO;

        /// <summary>Vertical offset from the origin of the reference grid to the top side of the image area.</summary>
        public uint YO;

        /// <summary>Width of one reference tile with respect to the reference grid.</summary>
        public uint XT;

        /// <summary>Height of one reference tile with respect to the reference grid</summary>
        public uint YT;

        /// <summary>Horizontal offset from the origin of the reference grid to the left side of the first tile.</summary>
        public uint XTO;

        /// <summary>Vertical offset from the origin of the reference grid to the top side of the first tile.</summary>
        public uint YTO;

        /// <summary>Number of components in the image</summary>
        public ushort C;

        public struct ComponentInfo
        {
            /// <summary>Precision (depth) in bits and sign of the ith component samples.</summary>
            public byte S;

            /// <summary>Horizontal separation of a sample the ith component with respect to the reference grid.</summary>
            public byte YR;

            /// <summary>Vertical separateion of a sample of ith component with respect to the reference grid.</summary>
            public byte XR;
        }
        /// <summary>One set for each component in Csiz.</summary>
        public ComponentInfo[] Component;

        /// <summary>Number of Tiles in this image.</summary>
        public uint TileCount
        {
            get
            {
                uint numXtiles = ((X - XTO) + XT - 1) / XT; // integer math equivalent of Ceiling((X - XTO) / XT)
                uint numYtiles = ((Y - YTO) + YT - 1) / YT; // integer math equivalent of Ceiling((Y - YTO) / YT)
                return (numXtiles * numYtiles);
            }
        }

        public SIZ(Stream source)
            : this(source, (Markers)StreamUtil.ReadBUInt16(source)) { }
        public SIZ(Stream source, Markers marker)
            : base(source, marker, true)
        {
            if (marker != Markers.SIZ) throw (new ApplicationException("Attempting to create segment of the wrong type."));

            R = (RValues)StreamUtil.ReadBUInt16(source);
            X = StreamUtil.ReadBUInt32(source);
            Y = StreamUtil.ReadBUInt32(source);
            Debug.WriteLine("Image Size: " + X + "," + Y);
            XO = StreamUtil.ReadBUInt32(source);
            YO = StreamUtil.ReadBUInt32(source);
            Debug.WriteLine("Image Offset: " + XO + "," + YO);
            XT = StreamUtil.ReadBUInt32(source);
            YT = StreamUtil.ReadBUInt32(source);
            Debug.WriteLine("Tile Size: " + XT + "," + YT);
            XTO = StreamUtil.ReadBUInt32(source);
            YTO = StreamUtil.ReadBUInt32(source);
            Debug.WriteLine("Tile Offset: " + XTO + "," + YTO);
            Debug.WriteLine("Number of Tiles: " + this.TileCount);

            C = StreamUtil.ReadBUInt16(source);
            Debug.WriteLine("Components: " + C);

            Debug.Indent();
            Component = new ComponentInfo[C];
            for (ushort i = 0; i < C; i++)
            {
                Component[i].S = StreamUtil.ReadOctet(source);
                Component[i].XR = StreamUtil.ReadOctet(source);
                Component[i].YR = StreamUtil.ReadOctet(source);
                Debug.WriteLine("Component " + i + " depth=" + (Component[i].S+1) + " separation=" + Component[i].XR + "," + Component[i].YR);
            }
            Debug.Unindent();
        }
    }
}
